#pragma once

#include <iostream>
#include <memory>
#include "Epoller.hpp"
#include "InetAddr.hpp"
#include "Socket.hpp"
#include "Log.hpp"
#include "Connection.hpp"
#include "IOService.hpp"
#include "Reactor.hpp"
#include "Protocol.hpp"
#include "Calculator.hpp"

using namespace SocketModule;
using namespace LogModule;
// 专门负责获取链接的模块

// 注意这个监听器，它是Connection的子类，也就是说它继承了Connection的所有成员变量和成员函数
// 也就是说它本质上其实也是一个套接字文件，只是这个套接字文件跟一般的通信套接字不一样
// 别人套接字文件的接收缓冲区中存的都是实打实的通信数据。
// 而监听套接字的接收缓冲区中存的则是一个个的来自四面八方客户端的请求报文。
class Listener : public Connection
{
public:
    // 
    Listener(int port)
        : _listensock(std::make_unique<TcpSocket>()),
          _port(port)
    {
        // 创建一个监听套接字的过程我们都比较熟悉
        // 首先要创建一个套接字。然后绑定端口，最后将这个绑定好的套接字设置为监听套接字
        // 下面这个函数就是对我们上面说的这个创建监听套接字的过程做了一个封装
        // 就是说你只需要给我这个端口号，剩下的事情就交给我这个函数调用来实现，具体怎么实现你就不用问了。
        _listensock->BuildTcpSocketMethod(_port);
        
        // 注意 监听器他本身也是一个连接，因此连接中的成员变量也是他的成员变量
        // 因此我们也要对这些变量做同步的初始化。
        SetSockfd(_listensock->Fd());
        SetEvents(EPOLLIN|EPOLLET);
    }
    // 监听套接字主要就是用来监听客户端的连接请求的，他的发送缓冲区基本没啥用
    // 因此我们的发送方法就直接空实现
    virtual void Sender() override
    {
    }
    
    // 监听套接字的读方法干的事情也比较直接
    // 就是调用accept，从这个监听套接字的全连接队列中取出一个链接，为这个连接专门创建一个通信套接字
    virtual void Recver() override
    {
        // 读就绪，而是是listensock就绪
        // IO处理 --- 获取新连接
        // 你怎么知道，一次来的，就是一个连接呢？你怎么保证你一次读完了么？
        while (true)
        {
            InetAddr peer;
            int aerrno = 0;
            // accept 非阻塞的时候，就是IO，我们就向处理read一样，处理accept
            int sockfd = _listensock->Accepter(&peer, &aerrno);
            // 走到这里，监听套接字就成功的将连接队列中的一个连接取出来了。
            // peer中记录的就是客户端的地址信息，aerrno中记录的就是链接的错误状态信息
            // sockfd 就是服务器为处理这个新连接的而创建的新套接字文件描述符
            if (sockfd > 0)
            {
                // 如果accept成功，说明我们已经成功为这个链接创建出了一个新的通信套接字文件，其文件描述符就是返回的sockfd
                // 我们要先将sockfd 添加到epoll中
                // 而epollserver只认connection
                // 因此我们要先将sockfd包装成为connection，然后再将connection添加到epollServer中
                LOG(LogLevel::DEBUG) << "Accepter success: " << sockfd;
                // 普通的文件描述符，处理IO的，也是connection！
                // 2. sockfd包装成为Connection！
                auto conn = std::make_shared<IOService>(sockfd);
                //将监听套接字的消息处理函数注册到该链接的成员变量 func_t on_message中
                conn->RegisterOnMessage(HandlerRequest); 
                // 因为connection有两种类型，一种是监听套接字，一种是通信套接字
                // 监听套接字的消息处理函数是用来处理客户端的连接请求的
                // 通信套接字的消息处理函数是用来处理客户端的通信数据的
                // 这俩是不一样的，因此要分开注册

                // 3. 插入到EpollServer
                GetOwner()->InsertConnection(conn);
            }
            else
            {
                // EAGAIN 和 EWOULDBLOCK 在现代系统中语义完全一致
                // 均表示“非阻塞 I/O 操作暂时无法完成，需重试”
                // 常见触发场景如下：
                    // read 操作：非阻塞 socket 的接收缓冲区为空，暂时无数据可读。
                    // write 操作：非阻塞 socket 的发送缓冲区已满，暂时无法写入数据。
                    // accept 操作：非阻塞监听 socket 暂时没有新的连接请求。
                    // connect 操作：非阻塞 socket 的连接正在建立中（尚未完成三次握手）。
                if (aerrno == EAGAIN || aerrno == EWOULDBLOCK)
                {
                    // 由于我们这里是监听套接字的读方法
                    // 因此出现EAGAIN或者 EWOULDBLOCK的原因只可能是监听套接字的接收缓冲区为空
                    LOG(LogLevel::DEBUG) << "accetper all connection ... done";
                    break;
                }
                else if (aerrno == EINTR)
                {
                    // EINTR 表示函数调用被信号中断
                    LOG(LogLevel::DEBUG) << "accetper intr by signal, continue";
                    continue;
                }
                else
                {
                    LOG(LogLevel::WARNING) << "accetper error ... Ignore";
                    break;
                }
            }
        }
    }
    virtual void Excepter() override
    {
    }
    int Sockfd() { return _listensock->Fd(); }
    ~Listener()
    {
        _listensock->Close();
    }

private:
    std::unique_ptr<Socket> _listensock;
    int _port;
};